﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Web;

namespace SaleDiscount.Portal.Common
{
    /// <summary>
    /// Date & Time Utility Methods
    /// </summary>
    public static class DateTimeUtil
    {
        #region TimeZone Retrieval
        /// <summary>
        /// Retrieves the timezone defined in the appSettings section with the key "TimeZone"
        /// </summary>
        private static TimeZoneInfo CurrentTimeZone
        {
            get
            {
                string timeZone;
                if (string.IsNullOrWhiteSpace(timeZone = ConfigurationManager.AppSettings["TimeZone"]))
                {
                    return TimeZoneInfo.Local;
                }
                return TimeZoneInfo.FindSystemTimeZoneById(timeZone.Trim());
            }
        }
        #endregion


        #region DateTime to String Conversion
        /// <summary>
        /// Formats a date/time object to the specified time zone
        /// </summary>
        /// <param name="dateTime">Date/Time to convert</param>
        /// <param name="timeZone">Destination time zone</param>
        /// <param name="format">Format</param>
        /// <param name="formatProvider">Formatting provider</param>
        /// <returns>Textual representation of the date/time in the specified timezone</returns>
        public static string ToString(this DateTime dateTime, TimeZoneInfo timeZone, string format = null, IFormatProvider formatProvider = null)
        {
            if (timeZone == null)
            {
                timeZone = TimeZoneInfo.Local;
            }
            if (formatProvider == null)
            {
                formatProvider = CultureInfo.CurrentCulture;
            }


            // Convert to the specified time zone
            dateTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);


            // Format
            if (format == null)
            {
                return dateTime.ToString(formatProvider);
            }
            else
            {
                return dateTime.ToString(format, formatProvider);
            }
        }
        #endregion


        #region Time Zone Text to UTC DateTime Parsing
        #region Normal Parsing
        /// <summary>
        /// Parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>DateTime object in Utc co-ordinates</returns>
        public static DateTime ParseToUtcTime(this TimeZoneInfo timeZone, string text, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (timeZone == null)
            {
                timeZone = TimeZoneInfo.Local;
            }
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            if (formatProvider == null)
            {
                formatProvider = CultureInfo.CurrentCulture;
            }


            return TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(DateTime.Parse(text, formatProvider, styles), DateTimeKind.Unspecified), timeZone);
        }


        /// <summary>
        /// Tries to parse the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="utcTime">Parsed time</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>True if the text could be parsed. False otherwise</returns>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public static bool TryParseToUtcTime(this TimeZoneInfo timeZone, string text, out DateTime utcTime, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (timeZone == null)
            {
                timeZone = TimeZoneInfo.Local;
            }
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            if (formatProvider == null)
            {
                formatProvider = CultureInfo.CurrentCulture;
            }


            if (DateTime.TryParse(text, formatProvider, styles, out utcTime))
            {
                try
                {
                    TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(utcTime, DateTimeKind.Unspecified), timeZone);
                    return true;
                }
                catch { }
            }
            return false;
        }
        #endregion


        #region Exact Parsing
        /// <summary>
        /// Parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="format">Date time format</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>DateTime object in Utc co-ordinates</returns>
        public static DateTime ParseExactToUtcTime(this TimeZoneInfo timeZone, string text, string format, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (timeZone == null)
            {
                timeZone = TimeZoneInfo.Local;
            }
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            if (format == null)
            {
                throw new ArgumentNullException("format");
            }
            if (formatProvider == null)
            {
                formatProvider = CultureInfo.CurrentCulture;
            }


            return TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(DateTime.ParseExact(text, format, formatProvider, styles), DateTimeKind.Unspecified), timeZone);
        }


        /// <summary>
        /// Tries to parse the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="format">Date time format</param>
        /// <param name="utcTime">Parsed time</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>True if the text could be parsed. False otherwise</returns>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public static bool TryParseExactToUtcTime(this TimeZoneInfo timeZone, string text, string format, out DateTime utcTime, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (timeZone == null)
            {
                timeZone = TimeZoneInfo.Local;
            }
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            if (format == null)
            {
                throw new ArgumentNullException("format");
            }
            if (formatProvider == null)
            {
                formatProvider = CultureInfo.CurrentCulture;
            }


            if (DateTime.TryParseExact(text, format, formatProvider, styles, out utcTime))
            {
                try
                {
                    TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(utcTime, DateTimeKind.Unspecified), timeZone);
                    return true;
                }
                catch { }
            }
            return false;
        }


        /// <summary>
        /// Parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="formats">Date time formats to try</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>DateTime object in Utc co-ordinates</returns>
        public static DateTime ParseExactToUtcTime(this TimeZoneInfo timeZone, string text, string[] formats, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (timeZone == null)
            {
                timeZone = TimeZoneInfo.Local;
            }
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            if (formats == null)
            {
                throw new ArgumentNullException("formats");
            }
            if (formatProvider == null)
            {
                formatProvider = CultureInfo.CurrentCulture;
            }


            return TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(DateTime.ParseExact(text, formats, formatProvider, styles), DateTimeKind.Unspecified), timeZone);
        }


        /// <summary>
        /// Tries to parse the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="formats">Date time formats to try</param>
        /// <param name="utcTime">Parsed time</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>True if the text could be parsed. False otherwise</returns>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public static bool TryParseExactToUtcTime(this TimeZoneInfo timeZone, string text, string[] formats, out DateTime utcTime, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (timeZone == null)
            {
                timeZone = TimeZoneInfo.Local;
            }
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            if (formats == null)
            {
                throw new ArgumentNullException("formats");
            }
            if (formatProvider == null)
            {
                formatProvider = CultureInfo.CurrentCulture;
            }


            if (DateTime.TryParseExact(text, formats, formatProvider, styles, out utcTime))
            {
                try
                {
                    TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(utcTime, DateTimeKind.Unspecified), timeZone);
                    return true;
                }
                catch { }
            }
            return false;
        }
        #endregion
        #endregion


        #region Tme Zone Text to Local DateTime Parsing
        #region Normal Parsing
        /// <summary>
        /// Parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>DateTime object in Local co-ordinates</returns>
        public static DateTime ParseToLocalTime(this TimeZoneInfo timeZone, string text, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            return ParseToUtcTime(timeZone, text, formatProvider, styles).ToLocalTime();
        }


        /// <summary>
        /// Tries to parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="localTime">Parsed time</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>True if the text could be parsed. False otherwise</returns>
        public static bool TryParseToLocalTime(this TimeZoneInfo timeZone, string text, out DateTime localTime, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (TryParseToUtcTime(timeZone, text, out localTime, formatProvider, styles))
            {
                localTime = localTime.ToLocalTime();
                return true;
            }
            return false;
        }
        #endregion


        #region Exact Parsing
        /// <summary>
        /// Parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="format">DateTime format</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>DateTime object in Local co-ordinates</returns>
        public static DateTime ParseExactToLocalTime(this TimeZoneInfo timeZone, string text, string format, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            return ParseExactToUtcTime(timeZone, text, format, formatProvider, styles).ToLocalTime();
        }


        /// <summary>
        /// Tries to parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="format">Date time format</param>
        /// <param name="localTime">Parsed time</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>True if the text could be parsed. False otherwise</returns>
        public static bool TryParseExactToLocalTime(this TimeZoneInfo timeZone, string text, string format, out DateTime localTime, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (TryParseExactToUtcTime(timeZone, text, format, out localTime, formatProvider, styles))
            {
                localTime = localTime.ToLocalTime();
                return true;
            }
            return false;
        }


        /// <summary>
        /// Parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="formats">DateTime formats to try</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>DateTime object in Local co-ordinates</returns>
        public static DateTime ParseExactToLocalTime(this TimeZoneInfo timeZone, string text, string[] formats, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            return ParseExactToUtcTime(timeZone, text, formats, formatProvider, styles).ToLocalTime();
        }


        /// <summary>
        /// Tries to parses the specified text in the specified timezone
        /// </summary>
        /// <param name="timeZone">Time zone for which the text date is specified</param>
        /// <param name="text">Date/time text to be parsed</param>
        /// <param name="formats">Date time formats to try</param>
        /// <param name="localTime">Parsed time</param>
        /// <param name="formatProvider">Formatting culture</param>
        /// <param name="styles">DateTime styles</param>
        /// <returns>True if the text could be parsed. False otherwise</returns>
        public static bool TryParseExactToLocalTime(this TimeZoneInfo timeZone, string text, string[] formats, out DateTime localTime, IFormatProvider formatProvider = null, DateTimeStyles styles = DateTimeStyles.None)
        {
            if (TryParseExactToUtcTime(timeZone, text, formats, out localTime, formatProvider, styles))
            {
                localTime = localTime.ToLocalTime();
                return true;
            }
            return false;
        }
        #endregion
        #endregion
    }
}
